//: C12:RefcountTrace.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Refcount.cpp w/ trace info
#include "../require.h"
#include <cstring>
#include <fstream>
using namespace std;

ofstream out("rctrace.out");

class Counted
{
    class MemBlock
    {
        static const int size = 100;
        char c[size];
        int refcount;
        static int blockcount;
        int blocknum;
    public:
        MemBlock() {
            memset(c, 1, size);
            refcount = 1;
            blocknum = blockcount++;
        }
        MemBlock(const MemBlock& rv) {
            memcpy(c, rv.c, size);
            refcount = 1;
            blocknum = blockcount++;
            print("copied block");
            out << endl;
            rv.print("from block");
        }
        ~MemBlock() {
            out << "\tdestroying block "
            << blocknum << endl;
        }
        void print(const char* msg = "") const {
            if (*msg) out << msg << ", ";
            out << "blocknum:" << blocknum;
            out << ", refcount:" << refcount;
        }
        void attach() {
            ++refcount;
        }
        void detach() {
            require(refcount != 0);
            // Destroy object if no one is using it:
            if (--refcount == 0) delete this;
        }
        int count() const {
            return refcount;
        }
        void set(char x) {
            memset(c, x, size);
        }
        // Conditionally copy this MemBlock.
        // Call before modifying the block; assign
        // resulting pointer to your block;
        MemBlock* unalias() {
            // Don't duplicate if not aliased:
            if (refcount == 1) return this;
            --refcount;
            // Use copy-constructor to duplicate:
            return new MemBlock(*this);
        }
    }* block;
    static const int sz = 30;
    char ident[sz];
public:
    Counted(const char* id = "tmp") {
        block = new MemBlock; // Sneak preview
        strncpy(ident, id, sz);
    }
    Counted(const Counted& rv) {
        block = rv.block; // Pointer assignment
        block->attach();
        strncpy(ident, rv.ident, sz);
        strncat(ident, " copy", sz - strlen(ident));
    }
    void unalias() {
        block = block->unalias();
    }
    void addname(const char* nm) {
        strncat(ident, nm, sz - strlen(ident));
    }
    Counted& operator=(const Counted& rv) {
        print("inside operator=\n\t");
        if (&rv == this) {
            out << "self-assignment" << endl;
            return *this;
        }
        // Clean up what you're using first:
        block->detach();
        block = rv.block; // Like copy-constructor
        block->attach();
        return *this;
    }
    // Decrement refcount, conditionally destroy
    ~Counted() {
        out << "preparing to destroy: " << ident
        << "\n\tdecrementing refcount ";
        block->print();
        out << endl;
        block->detach();
    }
    // Copy-on-write:
    void write(char value) {
        unalias();
        block->set(value);
    }
    void print(const char* msg = "") {
        if (*msg) out << msg << " ";
        out << "object " << ident << ": ";
        block->print();
        out << endl;
    }
};

int Counted::MemBlock::blockcount = 0;

int main()
{
    Counted A("A"), B("B");
    Counted C(A);
    C.addname(" (C) ");
    A.print();
    B.print();
    C.print();
    B = A;
    A.print("after assignment\n\t");
    B.print();
    out << "Assigning C = C" << endl;
    C = C;
    C.print("calling C.write('x')\n\t");
    C.write('x');
    out << "\n exiting main()" << endl;
} ///:~
